/****************************************************************************
 **
 ** This file is part of the yFiles extension package GraphML-3.2-yFiles-2.7.
 ** 
 ** yWorks proprietary/confidential. Use is subject to license terms.
 **
 ** Redistribution of this file or of an unauthorized byte-code version
 ** of this file is strictly forbidden.
 **
 ** Copyright (c) 2000-2009 by yWorks GmbH, Vor dem Kreuzberg 28, 
 ** 72070 Tuebingen, Germany. All rights reserved.
 **
 ***************************************************************************/

package demo.yext.graphml;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.StringWriter;

import javax.swing.AbstractAction;
import javax.swing.JComboBox;
import javax.swing.JMenu;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JToolBar;

import org.graphdrawing.graphml.GraphMLConstants;
import org.graphdrawing.graphml.writer.OutputHandler;
import org.graphdrawing.graphml.writer.XmlWriter;
import org.graphdrawing.graphml.writer.DomXmlWriter;
import org.graphdrawing.graphml.writer.GraphMLWriteException;


import y.module.YModule;
import y.util.D;
import y.util.DataProviderAdapter;
import yext.graphml.graph2D.GraphMLIOHandler;
import yext.graphml.processor.PostprocessorOutputHandler;


/**
 * This demo centers around postprocessing actions that can be specified
 * within a GraphML file. These actions allow to process 
 * the parsed graph structure before it gets returned by the GraphML parser. 
 * <p>
 * A GraphML processor can be any instance of the yFiles module class YModule.
 * The configuration of a processor is done by changing the values
 * managed by the associated OptionHandler instance. This demo allows to configure
 * a processor interactively. Furthermore, it can be used to display the GraphML
 * representation of a processor module configuration. 
 * When saving a file the XML representation of the current processor will be added
 * to the output file as well. When loading this file again,
 * the postprocessor will perform its action.
 * </p>
 */
public class PostprocessorDemo extends GraphMLDemo
{
  YModule processorModule;
  
  /** 
   * Creates a new instance of PostprocessorDemo 
   */
  public PostprocessorDemo()
  {
    //register a DataProvider that returns the selected
    //processor module. This dataprovider is used by
    //PostprocessorOutputHandler to lookup the postprocessors
    //it should serialize.
    view.getGraph2D().addDataProvider(PostprocessorOutputHandler.PROCESSORS_DPKEY,
        new DataProviderAdapter() {
          public Object get(Object graph)
          {
            return processorModule;
          }
        });
 
  }
  
  protected JToolBar createToolBar()
  {
    JToolBar jtb = super.createToolBar();
    jtb.addSeparator();
    
    //a combo box that contains the class names of available
    //postprocessors.  
    final JComboBox combo = new JComboBox(new String[] {
        "y.module.HierarchicLayoutModule",
        "y.module.SmartOrganicLayoutModule",
        "yext.graphml.processor.NodeSizeAdapter",
        "yext.graphml.processor.BackgroundConfigurator"
        }
    );
    
    combo.setEditable(true);
    jtb.add(combo);
    combo.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ev) {
        String className = combo.getSelectedItem().toString();
        try
        {
          processorModule = (YModule)Class.forName(className).newInstance();
        } catch(Exception ex)
        {
          D.showError("Can't create instance of class " + className);
        }
      }
    });
    combo.setSelectedIndex(0);
    
    jtb.add(new ConfigureProcessorAction());
    jtb.add(new ApplyProcessorAction());
    jtb.add(new ShowProcessorAction());
    
    return jtb;
  }

  protected JMenu createSampleGraphMenu() {
    String[] resources = {
      "resources/postprocessors/ant-build.graphml",
      "resources/postprocessors/food-owl.graphml",
    };
    return createSampleGraphMenu(resources);
  }

  /**
   * Creates a GraphMLIOHandler that has additional output support for 
   * GraphML postprocessors. 
   */
  protected GraphMLIOHandler createGraphMLIOHandler()
  {
    GraphMLIOHandler ioh = super.createGraphMLIOHandler();
    OutputHandler pout = new PostprocessorOutputHandler();
    ioh.getOutputHandlers(GraphMLConstants.SCOPE_GRAPH).add(pout);
    return ioh;
  }
  
  /**
   * Actions that allows to configure the selected postprocessor interactively.
   */
  class ConfigureProcessorAction extends AbstractAction
  {
    ConfigureProcessorAction()
    {
      super("Configure...");
    }
    
    public void actionPerformed(ActionEvent ev)
    {
      if(processorModule != null)
      {
        if(processorModule.getOptionHandler().showEditor())
        {
          processorModule.getOptionHandler().commitValues();
        }
      }
    }
  }

  /**
   * Actions that applies the selected processor on the displayed graph.
   */
  class ApplyProcessorAction extends AbstractAction
  {
    ApplyProcessorAction()
    {
      super("Apply");
    }
    
    public void actionPerformed(ActionEvent ev)
    {
      if(processorModule != null)
      {
        processorModule.start(view.getGraph2D());
        view.updateView();
      }
    }
  }
  

  /**
   * Actions that outputs the GraphML representation of the selected postprocessor
   * in a separate window.
   */
  class ShowProcessorAction extends AbstractAction
  {
    ShowProcessorAction()
    {
      super("Show GraphML");
    }
    
    public void actionPerformed(ActionEvent ev)
    {
      StringWriter sw = new StringWriter();
      XmlWriter writer = new DomXmlWriter( sw );
        writer.setWriteXmlDeclaration(false);
        writer.addNamespace(GraphMLConstants.YWORKS_EXT_NS_URI, "y");
        new PostprocessorOutputHandler().printDataOutput( null, view.getGraph2D(), null, writer );
        sw.flush();
        String xml = sw.toString();
        JTextArea textArea = new JTextArea(xml);
        JScrollPane scrollPane = new JScrollPane(textArea);
        scrollPane.setPreferredSize(new Dimension(600,400));
        JOptionPane.showMessageDialog(view, scrollPane);
    }
  }

  /**
   * Launches this demo.
   */
  public static void main(String[] args)
  {
    initLnF();
    final PostprocessorDemo demo = new PostprocessorDemo();
    demo.start();
  }
}
